package pl.easyshop.core.dao.common.hb;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import pl.easyshop.core.dao.common.GenericDAO;
import pl.easyshop.core.model.AbstractBaseModel;

/**
 * Abstract base class for Hibernate DAO implementations.
 * 
 * @author Pawel Witkowski
 * 
 * @param <T>
 *            - data model class.
 */
public abstract class HbAbstractGenericDAO<E extends AbstractBaseModel> implements GenericDAO<E> {

	/**
	 * Logger for class.
	 */
	private static final Logger log = Logger.getLogger(HbAbstractGenericDAO.class);

	@Autowired()
	@Qualifier("pgSessionFactory")
	private SessionFactory sessionFactory;

	@SuppressWarnings("unchecked")
	private final Class<E> getEntityClass() {
		return (Class<E>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
	protected Session getCurrentSession(){
		return sessionFactory.getCurrentSession();
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see pl.pwitkowski.cityguide.core.dao.GenericDAO#load(java.lang.Long)
	 */
	@SuppressWarnings("unchecked")
	public E load(Long id) {
		log.debug("Load model with id: " + id);
		E model = null;
		try {
			model = (E) sessionFactory.getCurrentSession().load(getEntityClass(), id);
		} catch (ObjectNotFoundException e) {
			log.warn("Object not found with id: " + id);
		}
		return model;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * pl.pwitkowski.cityguide.core.dao.GenericDAO#save(pl.pwitkowski.cityguide
	 * .core.model.BaseModel)
	 */
	public Long save(E model) {
		log.debug("Create model: " + model);
		sessionFactory.getCurrentSession().save(model);
		log.debug("Model created, id: " + model.getId());
		return model.getId();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * pl.pwitkowski.cityguide.core.dao.GenericDAO#update(pl.pwitkowski.cityguide
	 * .core.model.BaseModel)
	 */
	public void update(E model) {
		log.debug("Update model: " + model);
		sessionFactory.getCurrentSession().update(model);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see pl.pwitkowski.cityguide.core.dao.GenericDAO#delete(java.lang.Long)
	 */
	public void delete(Long id) {
		log.debug("Try delete model data. ID: " + id);
		E toDelete = load(id);
		log.debug("Delete model data: " + toDelete);
		sessionFactory.getCurrentSession().delete(toDelete);
	}

	/**
	 * Find data using {@link DetachedCriteria}.
	 * 
	 * @param criteria
	 *            Hibernate detached criteria.
	 * 
	 * @return list of found entity or empty list.
	 */
	@SuppressWarnings("unchecked")
	public List<E> find(DetachedCriteria detachedCriteria, int firstResult, int maxResults) {// return hibernateTemplate.findByCriteria(criteria, firstResult, maxResults);
		log.debug("detachedCriteria: " + detachedCriteria);
		Criteria criteria = detachedCriteria.getExecutableCriteria(sessionFactory.getCurrentSession()).setFirstResult(firstResult);
		if (maxResults >= 0) {
			criteria = criteria.setMaxResults(maxResults);
		}
		return criteria.list();
		
	}
	
	public List<E> find(DetachedCriteria criteria) {
	   return this.find(criteria, 0, -1);
	  }


}
